/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.api.utils;

import io.iec.edp.caf.commons.utils.InvokeService;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.rpc.api.InternalSvrContainer;
import io.iec.edp.caf.rpc.api.annotation.RpcParam;
import io.iec.edp.caf.rpc.api.entity.RpcServiceMethodDefinition;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.DefaultParameterNameDiscoverer;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.LinkedHashMap;

@Slf4j
public class Utils {
    public static InputStream handleStreamFromParameters(LinkedHashMap<String, Object> parameters){
        InputStream stream = null;
        for(var k:parameters.keySet()){
            //only support one stream parameter
            if(parameters.get(k) instanceof InputStream){
                stream = (InputStream)parameters.get(k);
                parameters.remove(k);
                break;
            }
        }
        return stream;
    }


    public static LinkedHashMap<String, Object> handleStreamParam(RpcServiceMethodDefinition rpcServiceMethodDefinition, LinkedHashMap<String, Object> params, InputStream streamParam){
        LinkedHashMap<String,Object> newParams = new LinkedHashMap<>();

        Object instance;
        String methodName = rpcServiceMethodDefinition.getId().substring(rpcServiceMethodDefinition.getId().lastIndexOf('.') + 1);
        String interfaceString = rpcServiceMethodDefinition.getParentDefinition().getClassName();
        Class iType;
        //注意此处需要传入线程持有的classLoader 否则对当前类的classLoader(LaunchedURLClassLoader)来说 SU中的Class将是不可见的
        iType = InvokeService.getClass(interfaceString);
        try{
            instance = SpringBeanUtils.getBean(iType);
        } catch (NoSuchBeanDefinitionException e){
            instance = InternalSvrContainer.getService(iType);
        }

        Class type = instance.getClass();
        if (type.getName().contains("$$EnhancerBySpring")) {
            type = type.getSuperclass();
        }

        //实例方法/接口方法
        Method method = RpcAppContextUtils.getMethodByName(type, methodName);
        Method iMethod = RpcAppContextUtils.getMethodByName(iType, methodName);

        //实例参数/接口参数
        Parameter[] parameters = method.getParameters();
        Parameter[] iParameters = iMethod.getParameters();

        if(log.isDebugEnabled()){
            log.debug("rpc method parameters count:"+parameters.length+",rpc interface parameters count:"+iParameters.length);
        }

        //方法参数名集合
        DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
        String[] paramsTruthNames = discover.getParameterNames(method);

        //handle stream parameters,one rpc service method only support one stream parameter
        //here bind stream parameter by method parameter`s @RpcParam
        for (int i = 0; i < parameters.length; i++) {
            Parameter iParameter = iParameters[i];
            //参数名称 这里判断是否有RpcParam注解 有的话从注解拿 没有的话从TruthName拿
            RpcParam rpcParam = iParameter.getDeclaredAnnotation(RpcParam.class);
            String paramName = rpcParam == null ? paramsTruthNames[i] : rpcParam.paramName();
            //if the parameter is InputStream or has @RpcParam(streamParam=true)
            if((rpcParam!=null && rpcParam.streamParam())
                    ||iParameter.getType().getName().equals(InputStream.class.getTypeName())){
                newParams.put(paramName,streamParam);
            }else{
                newParams.put(paramName,params.get(paramName));
            }
        }

        return newParams;
    }

}
